﻿#!/usr/bin/env python3
#encoding=utf-8


'''
Title is a descriptor class. 
descriptor的目的实际就是控制对class instance即object中data的读写访问。
通常instance的数据存储在instance里。
'''
class Title:    
    def __init__(self):
        self.title = "(n/a)"

    def __get__(self, instance, owner = None):
        if instance is None:       #class binding
            return "(n/a) due to no person attached."
        else:                       #instance binding
            return instance._title
    
    def __set__(self, instance, value):
        if instance is None:       #class binding
            pass        #nothing to do due to no person attached."
        else:                       #instance binding
            instance._title = value

    def __delete__(self, instance):
        if instance is None:    #class binding
            pass    #class attribute cannot be deleted.
        else:                   #instance binding
            del instance._title


'''
title is a descriptor. 
It is an attribute of Person object. However the attribute data is 
    stored in instance, and the descriptor just deals with get,set, 
    and del method which access the data of its owner instance.

'''
class Person:
    title = Title()
    def __init__(self):
        self._title="(n/a)"
       



def main():
    '''
    descriptor is the instance of the following class which implements __get__(), __set__(), or __delete__()。 
        class Descritpr:
            def __get__(self, instance, owner):
                pass
            def __set__(self, instance, value):
                pass
            def __delete__(self, instance):
                pass
    It used as the class owner's attribute. 

    Attribute access default lookup object's dictionary. for instance, 
        c.a lookup c.__dict__['a'] , then type(c).__dict__['a'], 
        and super class soso, exclude metaclass.
    However, if the lookup value a is one object defining one of the 
        methods, python may override the default behavior and invoke 
        the descriptor method instead.

    The starting point for descriptor invocation is a binding, a.x. How 
    the arguments are assembled depends on a:
        Direct Call
            The simplest and least common call is when user code directly
            invokes a descriptor method: x.__get__(a).
        Instance Binding
            If binding to an object instance, a.x is transformed into the 
            call: type(a).__dict__['x'].__get__(a, type(a)).
        Class Binding
            If binding to a class, A.x is transformed into the call: 
            A.__dict__['x'].__get__(None, A).
        Super Binding
            If a is an instance of super, then the binding super(B, obj).m() 
            searches obj.__class__.__mro__ for the base class A immediately 
            following B and then invokes the descriptor with the call: 
            A.__dict__['m'].__get__(obj, obj.__class__).        
    '''
    print("此例演示了descriptor机制。")
    person = Person()
    print("1. title is "+person.title)
    print("2. Person title is "+ Person.title)      #class binding

    person.title = "CEO"
    print("3. title is "+person.title)

    del person.title
    try:
        print("Err if run here!" + person.title)
    except AttributeError as ex:
        print(ex)
        print("OK! person._title already be deleted.")

    
    pass



if __name__=="__main__":
    main()


